/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dailydev.wave.calendar;

import static com.google.wave.api.EventType.*;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dailydev.wave.robot.EventHandlerOperation;
import org.dailydev.wave.robot.filter.ContentMatch;
import org.dailydev.wave.robot.filter.ContentMatchFilter;
import org.joda.time.DateTime;
import org.joda.time.IllegalFieldValueException;

import com.google.wave.api.Annotation;
import com.google.wave.api.Blip;
import com.google.wave.api.Context;
import com.google.wave.api.Event;
import com.google.wave.api.Range;
import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.TextView;
import com.google.wave.api.Wavelet;

/**
 * @author Roman Bosak, DailyDev.org
 */
public class CalendarRobotHandler {

    /**
     * 
     */
    protected static final String ANNOTATION_CALENDAR_DATE = "calendar/date";

    private static final String DATE_PATTERN = "((19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])";

    private static final Pattern datePattern = Pattern.compile(DATE_PATTERN);

    private static Logger log = Logger.getLogger(CalendarRobotHandler.class.getName());

    /**
     * http://www.google.com/googlecalendar/event_publisher_guide_detail.html
     * 
     * @param event
     * @param bundle
     * @throws UnsupportedEncodingException
     */
    @EventHandlerOperation(eventTypes = { BLIP_SUBMITTED, DOCUMENT_CHANGED })
    @ContentMatch(DATE_PATTERN)
    public void handleDateInDocument(Event event, RobotMessageBundle bundle, Map<String, Object> context)
            throws UnsupportedEncodingException {
        Blip blip = event.getBlip();
        removeAnnotations(blip);

        List<MatchResult> matchResults = (List<MatchResult>) context.get(ContentMatchFilter.CONTEXT_MATCH_RANGES);
        for (MatchResult matchResult : matchResults) {
            handleBlipMatch(blip, matchResult);
        }
    }

    /**
     * @param event
     * @param wavelet
     * @throws UnsupportedEncodingException
     */
    @EventHandlerOperation(eventTypes = { WAVELET_SELF_ADDED }, contexts = { Context.CHILDREN })
    @ContentMatch(DATE_PATTERN)
    public void handleDateInWavelet(Event event, Wavelet wavelet) throws UnsupportedEncodingException {
        Blip rootBlip = wavelet.getRootBlip();

        handleBlipRecursive(rootBlip);
    }

    /**
     * @param blip
     * @throws UnsupportedEncodingException
     */
    protected void handleBlipRecursive(Blip blip) throws UnsupportedEncodingException {
        handleBlip(blip);

        List<Blip> children = blip.getChildren();
        for (Blip child : children) {
            handleBlipRecursive(child);
        }
    }

    /**
     * @param blip
     * @throws UnsupportedEncodingException
     */
    protected void handleBlip(Blip blip) throws UnsupportedEncodingException {
        TextView textView = blip.getDocument();
        removeAnnotations(blip);

        Matcher matcher = datePattern.matcher(textView.getText());

        while (matcher.find()) {
            MatchResult matchResult = matcher.toMatchResult();
            handleBlipMatch(blip, matchResult);
        }
    }

    protected void removeAnnotations(Blip blip) {
        TextView textView = blip.getDocument();

        List<Annotation> annotations = textView.getAnnotations(ANNOTATION_CALENDAR_DATE);
        for (Annotation annotation : annotations) {
            textView.deleteAnnotations(annotation.getRange());
        }
    }

    /**
     * @param blip
     * @param matchResult
     * @throws UnsupportedEncodingException
     */
    protected void handleBlipMatch(Blip blip, MatchResult matchResult) throws UnsupportedEncodingException {
        Range range = new Range(matchResult.start(), matchResult.end());

        Integer year = Integer.valueOf(matchResult.group(1));
        Integer month = Integer.valueOf(matchResult.group(3));
        Integer date = Integer.valueOf(matchResult.group(4));

        String title = blip.getWavelet().getTitle();
        try {
            DateTime startTime = new DateTime(year, month, date, 12, 0, 0, 0);
            DateTime endTime = startTime.plusHours(1);
            List<String> participants = blip.getWavelet().getParticipants();
            TextView document = blip.getDocument();
            String details = document.getText();

            String uriString = buildNewEventUri(title, startTime, endTime, participants, details);

            document.setAnnotation(range, "link/manual", uriString);
            document.setAnnotation(range, ANNOTATION_CALENDAR_DATE, startTime.toString());
        } catch (IllegalFieldValueException e) {
            log.log(Level.WARNING, "Illegal date value '" + matchResult.group(0) + "': " + e.getMessage(), e);
        }
    }

    /**
     * @param title
     * @param startTime
     * @param endTime
     * @param participants
     * @param details
     * @return
     * @throws UnsupportedEncodingException
     */
    protected String buildNewEventUri(String title, DateTime startTime, DateTime endTime, List<String> participants,
            String details) throws UnsupportedEncodingException {
        StringBuffer uri = new StringBuffer("http://www.google.com/calendar/event?action=TEMPLATE");
        uri.append("&").append("text=").append(URLEncoder.encode(title, "UTF-8"));
        uri.append("&").append("dates=").append(startTime.toString("yyyyMMdd'T'HHmmss'Z'")).append("/").append(
                endTime.toString("yyyyMMdd'T'HHmmss'Z'"));
        for (String participant : participants) {
            uri.append("&").append("add=").append(URLEncoder.encode(participant, "UTF-8"));
        }

        if (details.length() > 500) {
            details = details.substring(0, 500);
        }

        uri.append("&").append("details=").append(URLEncoder.encode(details, "UTF-8"));
        uri.append("&").append("location=");
        uri.append("&").append("trp=false");
        uri.append("&").append("sprop=");
        uri.append("&").append("sprop=name:");
        String uriString = uri.toString();
        return uriString;
    }
}
